/* CsJni - .NET / Java Bridge
 * 
 * Copyright 2010 Robert E. Burke Licensed under the Apache License, Version 
 * 2.0 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at 
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable 
 * law or agreed to in writing, software distributed under the License is 
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 * KIND, either express or implied. See the License for the specific language 
 * governing permissions and limitations under the License. 
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using CsJni.PlatformAbstraction;
using System.IO;
using System.Collections.ObjectModel;

namespace CsJni
{
    public sealed class JavaVMDiscoveryStrategy
    {
        private JavaVMDiscoveryType mType;
        private string mSpec;
        private string[] mHints;

        public JavaVMDiscoveryStrategy(JavaVMDiscoveryType type, string spec, string[] hints)
        {
            mType = type;
            mSpec = spec;
            mHints = (hints == null) ? new string[0] : hints;
        }

        public JavaVMDiscoveryType DiscoveryType { get { return mType; } }
        public string Specification { get { return mSpec; } }
        public ReadOnlyCollection<String> Hints { get { return new ReadOnlyCollection<string>(mHints); } }
    }

    public sealed class JavaVMConfiguration
    {
        private List<JavaVMDiscoveryStrategy> mDiscoveryStrategies = new List<JavaVMDiscoveryStrategy>();
        // Windows (64-bit) - C:\Program Files\Java\jre6\bin\server\jvm.dll
        // OpenSuse - /usr/lib/jvm/jre-1.6.0/lib/i386/client/libjvm.so
        // Ubuntu - /usr/lib/jvm/java-6-sun/jre/lib/i386/client/libjvm.so
        // Mac - /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Libraries/libclient.dylib
        private JniVersion mVersion = JniVersion.Version1Minor4;
        private bool mIgnoreUnrecognized = true;
        private JavaVMOptionArray mJavaVMOptionArray = new JavaVMOptionArray();
        private string mCrtLib = "msvcrt.dll"; // "libc.so.6", "libc.dylib"
        private JavaVMLogTypes mLogTypes = JavaVMLogTypes.None;
        private List<String> mClassPathParts = new List<string>();
        private List<String> mLibraryPathParts = new List<string>();
        private List<String> mCounterpartPathParts = new List<string>();

        private JavaVMConfiguration()
        {
        }

        internal static JavaVMConfiguration CreateDefault()
        {
            return new JavaVMConfiguration();
        }

        public static JavaVMConfiguration FromXml(object node)
        {
            return JavaVMConfigurationSection.CreateFromXml((System.Xml.XmlNode)node);
        }

        public static JavaVMConfiguration FromAppConfiguration()
        {
            object sectionObj = System.Configuration.ConfigurationManager.GetSection("jvmInit");
            return ((JavaVMConfigurationSection)sectionObj).Configuration;
        }

        public int DiscoveryStrategyCount { get { return mDiscoveryStrategies.Count; } }

        public void AddDiscoveryStrategy(JavaVMDiscoveryStrategy strategy)
        {
            mDiscoveryStrategies.Add(strategy);
        }

        public JavaVMDiscoveryStrategy GetDiscoveryStrategyAtIndex(int index)
        {
            return mDiscoveryStrategies[index];
        }

        public JniVersion Version
        {
            get { return mVersion; }
            set { mVersion = value; }
        }

        public bool IgnoreUnrecognized
        {
            get { return mIgnoreUnrecognized; }
            set { mIgnoreUnrecognized = value; }
        }

        public void AddOption(string option)
        {
            mJavaVMOptionArray.AddOption(option);
        }

        internal void AddReservedOption(string option, IntPtr extraInfo)
        {
            mJavaVMOptionArray.AddReservedOption(option, extraInfo);
        }

        public string CrtLib
        {
            get { return mCrtLib; }
            set { mCrtLib = value; }
        }

        internal JavaVMOptionArray OptionArray
        {
            get { return mJavaVMOptionArray; }
        }

        public JavaVMLogTypes JavaVMLogTypes
        {
            get { return mLogTypes; }
            set { mLogTypes = value; }
        }

        public void AddClassPathPart(string part)
        {
            mClassPathParts.Add(part);
        }

        public void AddLibraryPathPart(string part)
        {
            mLibraryPathParts.Add(part);
        }

        public void AddCounterpartPathPart(string part)
        {
            mCounterpartPathParts.Add(part);
        }

        internal List<string> CounterpartPaths
        {
            get { return mCounterpartPathParts; }
        }

        internal void CanonicalizePaths(IPlatform platform)
        {
            CanonicalizePathsAndAddOption(platform, mClassPathParts, "-Djava.class.path=");
            CanonicalizePathsAndAddOption(platform, mLibraryPathParts, "-Djava.library.path=");
            mCounterpartPathParts = CanonicalizePathsHelper(platform, mCounterpartPathParts);
        }

        private void CanonicalizePathsAndAddOption(IPlatform platform, List<String> paths, String option)
        {
            String pathsConcat = String.Join(Path.PathSeparator.ToString(), CanonicalizePathsHelper(platform, paths).ToArray());
            AddReservedOption(option + pathsConcat, IntPtr.Zero);
        }

        private static List<string> CanonicalizePathsHelper(IPlatform platform, List<String> paths)
        {
            List<String> concatenatedPaths = new List<string>();
            foreach (String pathIter in paths)
            {
                string path = pathIter.Trim();
                if (path.Length == 0)
                {
                    throw new JniException(JniResourceManager.Instance.GetString("jvmCfgEmptyPath"));
                }
                path = platform.ExpandPath(path, ExpandPathType.Other);
                concatenatedPaths.Add(path);
            }
            return concatenatedPaths;
        }
    }
}
